#pragma once

#include "map/griddefines.h"
#include "dynamic/factory_holder.h"

enum MovementGeneratorType;
namespace battle::motion
{

    class MovementGenerator
    {
    public:
        virtual ~MovementGenerator();

        // called before adding movement generator to motion stack
        virtual void Initialize(battle::object::Unit&) = 0;
        // called aftre remove movement generator from motion stack
        virtual void Finalize(battle::object::Unit&) = 0;

        // called before lost top position (before push new movement generator above)
        virtual void Interrupt(battle::object::Unit&) = 0;
        // called after return movement generator to top position (after remove above movement generator)
        virtual void Reset(battle::object::Unit&) = 0;

        virtual bool Update(battle::object::Unit&, const unsigned int& time_diff) = 0;

        virtual MovementGeneratorType GetMovementGeneratorType() const = 0;

        virtual void unitSpeedChanged() {}

        // used by Evade code for select point to evade with expected restart default movement
        virtual bool GetResetPosition(battle::object::Unit&, float& /*x*/, float& /*y*/, float& /*z*/, float& /*o*/) const { return false; }

        // given destination unreachable? due to pathfinsing or other
        virtual bool IsReachable() const { return true; }

        // used for check from Update call is movegen still be active (top movement generator)
        // after some not safe for this calls
        bool IsActive(battle::object::Unit& u);
    };

    template<class T, class D>
    class MovementGeneratorMedium : public MovementGenerator
    {
    public:
        void Initialize(battle::object::Unit& u) override
        {
            // u->AssertIsType<T>();
            (static_cast<D*>(this))->Initialize(*((T*)&u));
        }
        void Finalize(battle::object::Unit& u) override
        {
            // u->AssertIsType<T>();
            (static_cast<D*>(this))->Finalize(*((T*)&u));
        }
        void Interrupt(battle::object::Unit& u) override
        {
            // u->AssertIsType<T>();
            (static_cast<D*>(this))->Interrupt(*((T*)&u));
        }
        void Reset(battle::object::Unit& u) override
        {
            // u->AssertIsType<T>();
            (static_cast<D*>(this))->Reset(*((T*)&u));
        }
        bool Update(battle::object::Unit& u, const unsigned int& time_diff) override
        {
            // u->AssertIsType<T>();
            return (static_cast<D*>(this))->Update(*((T*)&u), time_diff);
        }
        bool GetResetPosition(battle::object::Unit& u, float& x, float& y, float& z, float& o) const override
        {
            // u->AssertIsType<T>();
            return (static_cast<D const*>(this))->GetResetPosition(*((T*)&u), x, y, z, o);
        }
    public:
        // Will not link if not overridden in the generators
        void Initialize(T& u);
        void Finalize(T& u);
        void Interrupt(T& u);
        void Reset(T& u);
        bool Update(T& u, const unsigned int& time_diff);

        // not need always overwrites
        bool GetResetPosition(T& /*u*/, float& /*x*/, float& /*y*/, float& /*z*/, float& /*o*/) const { return false; }
    };

    struct SelectableMovement : public FactoryHolder<MovementGenerator, MovementGeneratorType>
    {
        SelectableMovement(MovementGeneratorType mgt) : FactoryHolder<MovementGenerator, MovementGeneratorType>(mgt) {}
    };

    template<class REAL_MOVEMENT>
    struct MovementGeneratorFactory : public SelectableMovement
    {
        MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) {}

        MovementGenerator* Create(void*) const override;
    };

    typedef FactoryHolder<MovementGenerator, MovementGeneratorType> MovementGeneratorCreator;
    typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
#if 0
    typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository;
#endif
}
